
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Runtime.Serialization;
using System.IO;
using System.Text;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.Container;
using Swaf.Logging;
using Swaf.Script;

namespace Swaf.BizObj.basic
{
	/// <summary>
	/// Implements the IBizObj interface by maintaining the bizobj values in an in
	/// memory dictionary.
	/// </summary>
	[Serializable]
	public class FullBizObj : BaseBizObj,ISerializable
	{
		public static bool s_logTypeMismatch = false;

		private static IClassFactory s_earlyBindFactory;

		protected IDictionary m_assignedFields;
		protected BitFlags m_dirtyFlags = new BitFlags();
		protected BitFlags m_validatedFlags = new BitFlags();
		protected bool m_isNew = true;
		protected bool m_isDeleted = false;

		/// <summary>
		/// Creates a new instance of the given bizobj type, without any of its fields set.
		/// </summary>
		/// <param name="bizObjType"></param>
		public FullBizObj(IBizObjDefinition bizObjType)
			:base(bizObjType)
		{
			//Intentially left blank.  Nothing to do here when initializing a blank fullbizobj.
		}

		public override IEnumerator GetEnumerator() { return m_assignedFields.GetEnumerator() ; }

		/// <summary>
		/// Given an XmlTextReader, this constructor will populate itself from the attributes and 
		/// elements that it finds while iterating through the XmlNodes from the current position.
		/// Once it reaches the end of its content it will return, leaving the XmlTextReader on the
		/// next element in its conent.
		/// </summary>
		/// <remarks>This method expects the XmlTextReader to be at the begining element of the
		/// Xml for this object.  If a new XmlTextReader is being used, ensure that the MoveToContent
		/// method is called at the very least or an exception will occur.</remarks>
		/// <param name="parentFieldName">If this is a child bizobj of a bizobj, the xml element
		/// that starts this bizobj would be named for the field, not the bizobj type.  If this is
		/// so, then this would be the expected node name for the main element. Can be null if
		/// the element will not be named for a field, or if a check is not desired to match
		/// the element name to the parentFieldName</param>
		/// <param name="info">The bizobj definition that this bizobj will become.</param>
		/// <param name="xml">The xml for this constructor to use to populate itself.</param>

		public FullBizObj(string parentFieldName, IBizObjDefinition info, XmlTextReader xml)
			: this(parentFieldName, info, xml, BaseBizObj.DefaultFieldDecoInfo)
		{
			//Intentionally left blank
		}

        public FullBizObj(XmlElement ele, string catalogName)
            :this(ele.OuterXml)
        {
        }


        public FullBizObj(string xml)
            : this((IBizObjDefinition)null)
        {
            XmlTextReader xmlIn = null;
            try
            {
                XmlNameTable nt = new NameTable();
                XmlNamespaceManager nsMgr = new XmlNamespaceManager(nt);
                XmlParserContext context = new XmlParserContext(nt, nsMgr, null, XmlSpace.None);
                xmlIn = new XmlTextReader(xml, XmlNodeType.Element, context);
                if (xmlIn.MoveToContent() == XmlNodeType.Element)
                    createFromStream(null, null, xmlIn, BaseBizObj.DefaultFieldDecoInfo);
            }
            catch (Exception e)
            {
                throw;
            }
            finally
            {
                xmlIn.Close();
            }
        }

		public FullBizObj(string parentFieldName, IBizObjDefinition info, XmlTextReader xml, string decoInfo)
			:this(info)
		{
            createFromStream(parentFieldName, info, xml, decoInfo);
        }

        protected void createFromStream(string parentFieldName, IBizObjDefinition info, XmlTextReader xml, string decoInfo)
        {
			if (info == null)
				info = m_bizObjType = Application.currentApp.bizObjMgr.getDefinition(xml.Name);

			if (xml.HasAttributes)
			{
				bool wasCursorAdvanced = false; // required for processSystemFlag call
				for (int i = 0; i < xml.AttributeCount; ++i)
				{
					xml.MoveToAttribute(i);
					BizObjField fld = info.Fields[xml.Name] as BizObjField;
					if (fld != null)
					{
						if (fld.BizType != BizFieldType.Method)
						{
							string xmlVal = xml.Value;
							putSingleNameProcessed(fld, xmlVal, decoInfo);
						}
					}
					else if(!processSystemFlag(xml, ref wasCursorAdvanced))
					{
						Application.globalApp.Log.write("Unknown attribute '{0}' was found while creating a '{1}' bizobj.  The attribute will be ignored",
							xml.Name, info.Name);
					}
				}
				xml.MoveToElement();
			}

			if (!xml.IsEmptyElement)
			{
				//See if this bizobj contains a default field
				if (this.type.Fields.Contains(""))
				{
					this.put("", getXmlContent(xml, (IBizObjField)info.Fields[""]));
				}
				else
				{
					bool done = false;
					bool wasCursorAdvanced = false;
					while (! done)
					{
						if (! wasCursorAdvanced)
						{
							done = ! xml.Read();
						}

						// Assume we'll have to read the next element after processing this one
						wasCursorAdvanced = false;

						if (xml.NodeType == XmlNodeType.Element)
						{
							string xmlName = xml.Name;
							BizObjField fld = info.Fields[xmlName] as BizObjField;
							if (fld != null)
							{
								if (fld.BizType != BizFieldType.Method)
								{
									object xmlVal = (wasCursorAdvanced = fld.doesUseFullXmlElement()) ?
										xml.ReadOuterXml() : getXmlContent(xml, fld);
                                                                        putSingleNameProcessed(fld, xmlVal, decoInfo);

									// ReadInnerXml and ReadOuterXml advance the cursor for us so don't do it at tope of loop
									//wasCursorAdvanced = true;
								}
							}
							else if(!processSystemFlag(xml, ref wasCursorAdvanced))
							{
								if (!tryToAddAsBizObjListItem(info, xml))
									Application.globalApp.Log.write("Unknown child element '{0}'was found while creating a '{1}' bizobj.  The element will be ignored",
											xml.Name, info.Name);
							}
						}
						else if (xml.NodeType == XmlNodeType.EndElement)
						{
							//Only break if this is the end element for the node associated with this
							//bizobj.  Do not break for child element end tags.
							if (xml.Name == info.Name || xml.Name == parentFieldName)
							{
								done = true;
								break;
							}
						}
					}
				}
			}
		}

		private bool processSystemFlag(XmlTextReader xml, ref bool wasCursorAdvanced)
		{
			bool processedFlag = true;
			if (xml.Name == BaseBizObj.DirtyFlagName)
				m_dirtyFlags = new BitFlags(xml.ReadInnerXml());
			else if (xml.Name == BaseBizObj.ValidatedFlagName)
				m_validatedFlags = new BitFlags(xml.ReadInnerXml());
			else if (xml.Name == BaseBizObj.IsNewFlagName)
				m_isNew = bool.Parse(xml.ReadInnerXml());
			else if (xml.Name == BaseBizObj.IsDeletedFlagName)
				m_isDeleted = bool.Parse(xml.ReadInnerXml());
			else
				processedFlag = false;

			// All impls used ReadInnerXml which moves the cursor to the next element
			wasCursorAdvanced = processedFlag;

			return processedFlag;
		}

		/// <summary>
		/// When a element is found during processing an XmlTextRead for a bizobj that does not match a field name directly, 
		/// this method is called to see if the element belongs as a child to a bizobj field that is a bizobj list.  
		/// Normally, if a bizobj Customer has a field called Addresses that is a bizobjlist of addresses, the Address elements
		/// will be in a parent element called Addresses.  However, incomming XML isn't required to having this and if an
		/// Address element is not contained within an Addresses parent, this method will attempt to create a new Address
		/// bizobj and push it into the Addresses field of the current bizobj being populated.
		/// </summary>
		/// <param name="info">The bizobj definition of the current bizobj being populated.  </param>
		/// <param name="xml">The XmlTextReader positioned at the element to be examinined</param>
		/// <returns>true if was able to find a matching bizobjlist and properly populate it, false
		/// if it was not.</returns>
		private bool tryToAddAsBizObjListItem(IBizObjDefinition info, XmlTextReader xml)
		{
			bool matchFound = false;
			IApplication app = Application.globalApp;
			string xmlName = xml.Name;
			IBizObjList lst;
			//See if it is a valid bizobj type
			try
			{
				IBizObjDefinition def = app.bizObjMgr.getDefinition(xmlName);
				if (def != null)
				{
					//See if you can find a BizObjList in field list of this type.
					foreach (System.Collections.DictionaryEntry fld in info.Fields)
					{
						BizObjField lstField = (BizObjField)fld.Value;
						if (lstField.BizType == BizFieldType.BizObjList)
						{
							lst = (IBizObjList)this.get(lstField.Name);
							if (lst.objectType.Name.Equals(xmlName))
							{
								lst.insertNewMakeCopy(FullBizObj.createInstance(xmlName, def, xml));
								matchFound = true;
								break;
							}
						}
					}
				}
			}
			catch (Exception e)
			{
				app.Trace.write(TraceLevels.Normal, "Encountered error trying to create bizobj element for ndoe {0} on bizobj {1}.  Error was {2}", xmlName, info.Name, e.ToString());
			}
			return matchFound;
		}

		protected bool isAllWhitespace(StringBuilder buff)
		{
			if (buff.Length != 0)
			{
				int buffLen = buff.Length;
				for (int p = 0; p < buffLen; ++p)
					if (!char.IsWhiteSpace(buff[p]))
						return false;
			}
			return true;
		}

		protected string getXmlContent(XmlTextReader xml, IBizObjField fld)
		{
			if (xml.NodeType == XmlNodeType.Attribute)
				return xml.Value; //.Length==0?null:xml.Value;

			StringBuilder buff = null;
			getElementContent(xml, ref buff, fld);
			//return (buff == null)?null:buff.Length==0?null:buff.ToString();
			return (buff == null) ? null : buff.ToString();
		}

		protected void getElementContent(XmlTextReader xml, ref StringBuilder buff, IBizObjField fld)
		{
			if (buff == null)
				buff = new StringBuilder();
			if (!xml.IsEmptyElement)
			{
				IDecorator d = fld == null ? null : (IDecorator)fld.Decorators["xmlOut"];
				if (d != null && string.Compare("rawElement", d.Value, true) == 0)
					buff.Append(xml.ReadInnerXml());
				else if (BizObjManager.NullAsElementAttribute && xml.GetAttribute(BizObjManager.NullValueIndicatorName) != null && string.Compare(xml.GetAttribute(BizObjManager.NullValueIndicatorName), "true", true) == 0)
					buff = null;
				else
					while (xml.Read())
					{
						if (buff != null &&
							(xml.NodeType == XmlNodeType.Text || xml.NodeType == XmlNodeType.SignificantWhitespace ||
							xml.NodeType == XmlNodeType.Whitespace || xml.NodeType == XmlNodeType.CDATA))
						{
							buff.Append(xml.Value);
						}
						else if (xml.NodeType == XmlNodeType.Element)
						{
							if (xml.Name == BizObjManager.NullValueIndicatorName && isAllWhitespace(buff))
							{
								buff = null;
							}
							else
								getElementContent(xml, ref buff, fld);
						}
						else if (xml.NodeType == XmlNodeType.EndElement)
						{
							//xml.ReadEndElement();
							break;
						}
					}
			}
			else
			{
				if (BizObjManager.NullAsElementAttribute && xml.GetAttribute(BizObjManager.NullValueIndicatorName) != null && string.Compare(xml.GetAttribute(BizObjManager.NullValueIndicatorName), "true", true) == 0)
					buff = null;
				else
					buff.Append("");
			}
		}

		/// <summary>
		/// This (or one of the other static createInstance methods) is the proper way to create new instances
		/// of a FullBizObj.  If early bound interfaces and instances are being used, this method will create
		/// one of those for the given bizobj type rather then creating a FullBizObj directly.  For example,
		/// if early bound bizobj implementations are being used, a "Customer" bizobj will have an ICustomer/Customer
		/// interface/class created for it.  Rather then creating a FullBizObj instance, it will create a
		/// Customer instance.
		/// </summary>
		/// <param name="def">The bizobj type ot create.</param>
		/// <returns>a new instance of the bizobj type requested.</returns>
		public static IBizObj createInstance(IBizObjDefinition def)
		{
			IBizObj obj = new FullBizObj(def);
			if (EarlyBindFactory != null)
				try
				{
					obj = (IBizObj)EarlyBindFactory.createFromNickName(def.Name, obj);
				}
				catch
				{
					//Intentionally left blank.  If an exception occurs while creating an
					//early bound, the late bound instance will be returned.
				}
			return obj;
		}

		/// <summary>
		/// This (or one of the other static createInstance methods) is the proper way to create new instances
		/// of a FullBizObj.  If early bound interfaces and instances are being used, this method will create
		/// one of those for the given bizobj type rather then creating a FullBizObj directly.  For example,
		/// if early bound bizobj implementations are being used, a "Customer" bizobj will have an ICustomer/Customer
		/// interface/class created for it.  Rather then creating a FullBizObj instance, it will create a
		/// Customer instance.
		/// </summary>
		/// <param name="parentName">the parent field name this instance is associated with.</param>
		public static IBizObj createInstance(string parentName, IBizObjDefinition info, XmlTextReader xml)
		{
			return FullBizObj.createInstance(parentName, info, xml, BaseBizObj.DefaultFieldDecoInfo);
		}
		public static IBizObj createInstance(string parentName, IBizObjDefinition info, XmlTextReader xml, string decoInfo)
		{
			IBizObj obj = new FullBizObj(parentName, info, xml, decoInfo);
			if (EarlyBindFactory != null)
				try
				{
					obj = (IBizObj)EarlyBindFactory.createFromNickName(info.Name, obj);
				}
				catch { }
			return obj;
		}

		/// <summary>
		/// This is the early binding class factory that will exist if early binding bizobj
		/// support is being used.  If the early binding bizobj support is not present, then
		/// this properly will return null on get.
		/// </summary>
		public static IClassFactory EarlyBindFactory
		{
			get { return s_earlyBindFactory; }
			set { s_earlyBindFactory = value; }
		}

		public static void  resetFactoryReferences()
		{
			 s_earlyBindFactory = null;
		}

		/// <summary>
		/// This method checks if the bizobj has a value assigned to the specified field.
		/// This method does not check for a valid field name being supplied. If an invalid
		/// field name is supplied this method will always return false.
		/// </summary>
		/// <param name="singleName">The field in this bizobj to check for an assigned value.</param>
		/// <returns>true if the given field has been assigned a value, false if not.</returns>
		public override bool containsSingleField(string singleName)
		{
            IBizObjField fld = m_bizObjType.getField(singleName, false);
            if (fld == null)
                return false;
            if (fld.BizType == BizFieldType.Method)
                return true;
            else
			    return m_assignedFields==null?false:m_assignedFields.Contains(singleName);
		}

		/// <summary>
		/// If this bizobj has an assigned value to the specified field name, this method
		/// will remove the assigned value from the bizobj and reset the dirty/validated
		/// flags.  This method does not support deep names (a.b.c).  It is called by basebizobj
		/// which supports deep names and uses this method appropriately.
		/// </summary>
		/// <param name="singleName">the field in this bizobj to remove the value for.</param>
		protected override void removeValue(string singleName)
		{
			BizObjField field = m_bizObjType.getField(singleName, true) as BizObjField;
			if (field != null && m_assignedFields != null && m_assignedFields.Contains(singleName))
			{
				m_assignedFields.Remove(singleName);
				m_dirtyFlags.setBit(field.BizObjIndex, false);
				m_validatedFlags.setBit(field.BizObjIndex, false);
			}
		}

		protected override BitFlags DirtyFlags { get { return m_dirtyFlags; } }
		protected override BitFlags ValidatedFlags { get { return m_validatedFlags; } }

		/// <summary>
		/// If this bizobj is marked as "new" then this will return true, else false.
		/// </summary>
		public override bool isNew
		{
			get { return m_isNew; }
			set { m_isNew = value; }
		}

		/// <summary>
		/// If this bizobj is marked as "delete" then this will return true, else false.
		/// </summary>
		public override bool isDeleted
		{
			get { return m_isDeleted; }
			set { m_isDeleted = value; }
		}

		/// <summary>
		/// This is a deep clone method, which goes through the serializing this as XML
		/// and then recreating it via the constructor that populates from an XmlTextReader.
		/// This is the best approach to absolutely ensure a fully deep clone is done and that no
		/// shared values exist between this and the newly created clone.
		/// </summary>
		/// <returns>The deeply cloned bizobj</returns>
		public override object Clone()
		{
			XmlTextReader xml = new XmlTextReader(new StringReader(getXml("all setOnly")));
			xml.MoveToContent();
			return new FullBizObj(null, m_bizObjType, xml);
		}

        protected override bool isSingleFieldSet(string singleName)
        {
			object val = null;
			if (m_assignedFields != null && m_assignedFields.Contains(singleName))
			{
				val = m_assignedFields[singleName];
				if (val == null)
					return m_bizObjType.getField(singleName, true).DecoratedSampleValue == null;
				return !m_assignedFields[singleName].Equals(m_bizObjType.getField(singleName, true).DecoratedSampleValue);
			}
			return false;
        }

		/// <summary>
		/// Used by BaseBizObj to get the value of a field for this bizobj.  This method
		/// does not support deep names (a.b.c) and expects the field name an actual direct
		/// field of this bizobj.  If not, nothing happens.  BaseBizObj is expected to verify
		/// the name is really a field of the bizobj.
		/// </summary>
		/// <param name="singleName">The name of the field to get the value of.</param>
		/// <returns>The raw value of the object, if decorated it will still be decorated.</returns>
		protected override object getSingleName(string singleName)
		{
			object val = null;
			if (m_assignedFields != null && m_assignedFields.Contains(singleName))
				val = m_assignedFields[singleName];
			else
			{
				IBizObjField field = m_bizObjType.getField(singleName, true);
				if (field.BizType == BizFieldType.Method)
					val = callScriptMethod(field, new object[0], true);
				else
				{
					val = field.DecoratedSampleValue;
					if (val != null)
					{
						if (val is ICloneable)
							val = ((ICloneable)val).Clone();
						else
							val = BizObjFieldFactory.createFromNickName(field.FactoryType, val);

						if (m_assignedFields == null)
							lock (this)
								if (m_assignedFields == null)
									m_assignedFields = new Hashtable(new CaseInsensitiveHashCodeProvider(), new CaseInsensitiveComparer());
						m_assignedFields[singleName] = val;
					}
				}
			}
			//if (val is INameAddressSupport)
			//    val = ((INameAddressSupport)val).get(null);
			return val;
		}

		protected object callScriptMethod(IBizObjField field, object[] infoArgs, bool isGet)
		{
			ScriptContainerNode scriptNode = field.SampleValue as ScriptContainerNode;
			if (scriptNode == null)
				return null;
			int numPassedArgs = infoArgs.Length + 2;
			int paramsToPass = numPassedArgs;
			if (isGet && numPassedArgs == scriptNode.Parameters.Length + 1 && (infoArgs[infoArgs.Length - 1] is string || infoArgs[infoArgs.Length - 1] == null))
				--paramsToPass;

			object[] newArgs = new object[paramsToPass];
			newArgs[0] = this;
			newArgs[1] = Application.currentApp;
			Array.Copy(infoArgs, 0, newArgs, 2, paramsToPass - 2);
			if (isGet)
				return scriptNode.get(null, newArgs);
			else
				scriptNode.put(null, newArgs);
			return null;
		}

		/// <summary>
		/// Used by BaseBizObj to get the value of a field for this bizobj.  This method
		/// does not support deep names (a.b.c) and expects the field name an actual direct
		/// field of this bizobj.  If not, nothing happens.  BaseBizObj is expected to verify
		/// the name is really a field of the bizobj.
		/// </summary>
		/// <param name="singleName">The name of the field to get the value of.</param>
		/// <returns>The raw value of the object, if decorated it will still be decorated.</returns>
		protected override object getSingleName(string singleName, object[] extra)
		{
			object val = null;
			if (m_assignedFields != null && m_assignedFields.Contains(singleName))
				val = m_assignedFields[singleName];
			else
			{
				IBizObjField field = m_bizObjType.getField(singleName, true);
				if (field.BizType == BizFieldType.Method)
					val = callScriptMethod(field, extra, true);
				else
				{
					val = field.DecoratedSampleValue;
					if (val != null)
					{
						if (val is ICloneable)
							val = ((ICloneable)val).Clone();
						else
							val = BizObjFieldFactory.createFromNickName(field.FactoryType, val);
						if (m_assignedFields == null)
							lock (this)
								if (m_assignedFields == null)
									m_assignedFields = new Hashtable(new CaseInsensitiveHashCodeProvider(), new CaseInsensitiveComparer());
						m_assignedFields[singleName] = val;
					}
				}
			}

			return val;
		}

		/// <summary>
		/// Used by BaseBizObj to put the value of a field for this bizobj.  This method
		/// does not support deep names (a.b.c) and expects the field name an actual direct
		/// field of this bizobj.  If not, nothing happens.  BaseBizObj is expected to verify
		/// the name is really a field of the bizobj.
		/// </summary>
		/// <param name="singleName">The name of the field to get the value of.</param>
		/// <param name="val">The value to be put into the bizobj, this should be a post
		/// decorator value.</param>
		/// <param name="shouldValueBeFieldType">If true, this method will attempt to verify
		/// the .NET type of val matches the expected type of the field.  If not it will
		/// log the issue but still put the value in its dictionary.  For example, if an Int32
		/// is passed by an Int16 is expected a log entry will be written and the Int32 will be 
		/// put in the dictionary.</param>
		protected override void putSingleName(string singleName, object val, object extraInfo, bool shouldValueBeFieldType)
		{
			if (m_assignedFields == null)
				lock(this)
					if(m_assignedFields == null)
						m_assignedFields = new Hashtable(new CaseInsensitiveHashCodeProvider(), new CaseInsensitiveComparer());
			BizObjField field = m_bizObjType.getField(singleName, true) as BizObjField;
			if (field.BizType != BizFieldType.Method)
			{
				if(shouldValueBeFieldType && (val != null) && (field.PlatformType != val.GetType()) && s_logTypeMismatch)
				{
					Application.globalApp.Log.write(
						String.Format("Putting a different type into bizobj '{0}.{1}'. Expected type is '{2}', but being put with  '{3}'",
						m_bizObjType.Name, singleName, field.PlatformType.Name, val.GetType().Name));
				}
				m_assignedFields[field.Name] = val;
			}
			else
				callScriptMethod(field, new object[] { val }, false);
		}

		/// <summary>
		/// This method will attempt to convert the given value (val) into the type expected
		/// by the field.  This method does NOT perform magic.  If the field type doesn't support
		/// being created by the type passed this method will fail.  IF the field is a bizobj or
		/// bizobjlist type, it will use val as a string and trying processing it as XML.  If this
		/// method cannot convert the type, it will log an entry in the application log.
		/// </summary>
		/// <param name="field">The bizobj field definition to use for the destination type.</param>
		/// <param name="val">The source value to convert from.</param>
		/// <returns>The converted type if possible or the origional type if the conversion could not
		/// be done.</returns>
		protected override object createPlatformTypeInstance(BizObjField field, object val)
		{
			try
			{
				if (field.BizType == BizFieldType.NetClass)
				{
					if (val != null)
					{
						if (val is string && ((string)val).Length == 0)
							val = BizObjFieldFactory.createFromNickName(field.FactoryType);
						else
							val = BizObjFieldFactory.createFromNickName(field.FactoryType, val);
					}
				}
				else if (field.BizType == BizFieldType.BizObj)
				{
					IBizObjDefinition def = Application.globalApp.bizObjMgr.getDefinition(field.FactoryType);
					if (def != null)
					{
						XmlTextReader xml = new XmlTextReader(new StringReader(val.ToString()));
						xml.MoveToContent();
						val = FullBizObj.createInstance(def.Name, def, xml);
					}
				}
				else if (field.BizType == BizFieldType.BizObjList)
				{
					XmlTextReader xml = new XmlTextReader(new StringReader(val.ToString()));
					xml.MoveToContent();
					val = FullBizObjList.createInstance(null, null, xml);
				}
			}
			catch
			{
				//Changed the catch to finally.  Testing to see if it really should throw exception over it.
				//If val cannot be converted to the platform type of the field, the best thing to do
				//is to let val be returned unconverted.  
				Application.globalApp.Log.write(String.Format("Unable to convert a '{0}' to a '{1}' for bizobj field '{2}.{3}'",
					(val == null) ? "null" : val.GetType().Name, field.PlatformType.Name, field.Parent.Name, field.Name));
			}

			return val;
		}

		public FullBizObj(System.Runtime.Serialization.SerializationInfo info, StreamingContext context)
            :base(null)
		{
			m_bizObjType = Application.globalApp.bizObjMgr.getDefinition(info.GetString("n"));
		    m_assignedFields = (IDictionary)info.GetValue("a", typeof(System.Collections.Hashtable));
		    m_dirtyFlags = (BitFlags)info.GetValue("d", typeof(Swaf.BitFlags));
		    m_validatedFlags = (BitFlags)info.GetValue("v", typeof(Swaf.BitFlags));
		    m_isNew = info.GetBoolean("in");
		    m_isDeleted = info.GetBoolean("id");
		}

        public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, StreamingContext context)
		{
			info.AddValue("n", m_bizObjType.Name);
            info.AddValue("a", m_assignedFields);
            info.AddValue("d", m_dirtyFlags);
            info.AddValue("v", m_validatedFlags);
            info.AddValue("in", m_isNew);
            info.AddValue("id", m_isDeleted);
		}

	}
}
